/*********************************************************************************************************************
 * CYT4BB Opensourec Library 即（ CYT4BB 开源库）是一个基于官方 SDK
 *接口的第三方开源库 Copyright (c) 2022 SEEKFREE 逐飞科技
 *
 * 本文件是 CYT4BB 开源库的一部分
 *
 * CYT4BB 开源库 是免费软件
 * 您可以根据自由软件基金会发布的 GPL（GNU General Public License，即
 *GNU通用公共许可证）的条款 即 GPL 的第3版（即
 *GPL3.0）或（您选择的）任何后来的版本，重新发布和/或修改它
 *
 * 本开源库的发布是希望它能发挥作用，但并未对其作任何的保证
 * 甚至没有隐含的适销性或适合特定用途的保证
 * 更多细节请参见 GPL
 *
 * 您应该在收到本开源库的同时收到一份 GPL 的副本
 * 如果没有，请参阅<https://www.gnu.org/licenses/>
 *
 * 额外注明：
 * 本开源库使用 GPL3.0 开源许可证协议 以上许可申明为译文版本
 * 许可申明英文版在 libraries/doc 文件夹下的 GPL3_permission_statement.txt
 *文件中 许可证副本在 libraries 文件夹下 即该文件夹下的 LICENSE 文件
 * 欢迎各位使用并传播本程序 但修改内容时必须保留逐飞科技的版权声明（即本声明）
 *
 * 文件名称          zf_driver_spi
 * 公司名称          成都逐飞科技有限公司
 * 版本信息          查看 libraries/doc 文件夹内 version 文件 版本说明
 * 开发环境          IAR 9.40.1
 * 适用平台          CYT4BB
 * 店铺链接          https://seekfree.taobao.com/
 *
 * 修改记录
 * 日期              作者                备注
 * 2024-1-9       pudding            first version
 ********************************************************************************************************************/

#include "zf_driver_spi.h"
#include "gpio/cy_gpio.h"
#include "scb/cy_scb_spi.h"
#include "sysclk/cy_sysclk.h"
#include "zf_common_debug.h"
#include "zf_driver_delay.h"
#include "zf_driver_gpio.h"

#define SPI_FREQ CY_INITIAL_TARGET_PERI_FREQ // 串口模块时钟 默认80M

volatile stc_SCB_t *spi_module[4] = {SCB7, SCB8, SCB9, SCB6};
spi_cs_pin_enum cs_pin_save[4];
//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI获取时钟引脚号
// 参数说明       clk_pin     时钟引脚 参照 zf_driver_spi.h 内 spi_clk_pin_enum
// 枚举体定义 返回参数       void 使用示例 spi_get_clk_pin(SPI0_CLK_P02_2);
// 备注信息       内部调用，用户无需关心
//-------------------------------------------------------------------------------------------------------------------
static gpio_pin_enum spi_get_clk_pin(spi_clk_pin_enum clk_pin) {
  gpio_pin_enum temp_clk_pin = NC;

  switch (clk_pin) {
  case SPI0_CLK_P02_2:
    temp_clk_pin = P02_2;
    break;
  case SPI1_CLK_P12_2:
    temp_clk_pin = P12_2;
    break;
  case SPI2_CLK_P15_2:
    temp_clk_pin = P15_2;
    break;
  case SPI6_CLK_P03_2:
    temp_clk_pin = P03_2;
    break;
  }

  return temp_clk_pin;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI获取MOSI引脚号
// 参数说明       mosi_pin     时钟引脚 参照 zf_driver_spi.h 内
// spi_mosi_pin_enum 枚举体定义 返回参数       void 使用示例
// spi_get_mosi_pin(SPI0_MOSI_P02_1); 备注信息       内部调用，用户无需关心
//-------------------------------------------------------------------------------------------------------------------
static gpio_pin_enum spi_get_mosi_pin(spi_mosi_pin_enum mosi_pin) {
  gpio_pin_enum temp_mosi_pin = NC;

  switch (mosi_pin) {
  case SPI0_MOSI_P02_1:
    temp_mosi_pin = P02_1;
    break;
  case SPI1_MOSI_P12_1:
    temp_mosi_pin = P12_1;
    break;
  case SPI2_MOSI_P15_1:
    temp_mosi_pin = P15_1;
    break;
  case SPI6_MOSI_P03_1:
    temp_mosi_pin = P03_1;
    break;
  }

  return temp_mosi_pin;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI获取MISO引脚号
// 参数说明       miso_pin     时钟引脚 参照 zf_driver_spi.h 内
// spi_miso_pin_enum 枚举体定义 返回参数       void 使用示例
// spi_get_miso_pin(SPI0_MISO_P02_0); 备注信息       内部调用，用户无需关心
//-------------------------------------------------------------------------------------------------------------------
static gpio_pin_enum spi_get_miso_pin(spi_miso_pin_enum miso_pin) {
  gpio_pin_enum temp_miso_pin = NC;

  switch (miso_pin) {
  case SPI0_MISO_P02_0:
    temp_miso_pin = P02_0;
    break;
  case SPI1_MISO_P12_0:
    temp_miso_pin = P12_0;
    break;
  case SPI2_MISO_P15_0:
    temp_miso_pin = P15_0;
    break;
  case SPI6_MISO_P3_0:
    temp_miso_pin = P03_0;
    break;
  }

  return temp_miso_pin;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI获取CS引脚号
// 参数说明       clk_pin     时钟引脚 参照 zf_driver_spi.h 内 spi_clk_pin_enum
// 枚举体定义 返回参数       void 使用示例 spi_get_clk_pin(SPI0_CS0_P02_3);
// 备注信息       内部调用，用户无需关心
//-------------------------------------------------------------------------------------------------------------------
static gpio_pin_enum spi_get_cs_pin(spi_cs_pin_enum cs_pin) {
  gpio_pin_enum temp_cs_pin = NC;
  switch (cs_pin) {
  case SPI0_CS0_P02_3:
    temp_cs_pin = P02_3;
    break;
  case SPI0_CS1_P02_4:
    temp_cs_pin = P02_4;
    break;
  case SPI1_CS0_P12_3:
    temp_cs_pin = P12_3;
    break;
  case SPI1_CS1_P12_4:
    temp_cs_pin = P12_4;
    break;
  case SPI2_CS0_P15_3:
    temp_cs_pin = P15_3;
    break;
  case SPI2_CS3_P05_1:
    temp_cs_pin = P05_1;
    break;
  case SPI6_CS_P03_3:
    temp_cs_pin = P03_3;
    break;
  }
  return temp_cs_pin;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI获取时钟引脚复用关系
// 参数说明       clk_pin     时钟引脚 参照 zf_driver_spi.h 内 spi_clk_pin_enum
// 枚举体定义 返回参数       void 使用示例 spi_get_clk_pin(SPI0_CLK_P02_2);
// 备注信息       内部调用，用户无需关心
//-------------------------------------------------------------------------------------------------------------------
static en_hsiom_sel_t spi_get_clk_hsiom(spi_clk_pin_enum clk_pin) {
  en_hsiom_sel_t temp_clk_hsiom = HSIOM_SEL_GPIO;

  switch (clk_pin) {
  case SPI0_CLK_P02_2:
    temp_clk_hsiom = P2_2_SCB7_SPI_CLK;
    break;
  case SPI1_CLK_P12_2:
    temp_clk_hsiom = P12_2_SCB8_SPI_CLK;
    break;
  case SPI2_CLK_P15_2:
    temp_clk_hsiom = P15_2_SCB9_SPI_CLK;
    break;
  case SPI6_CLK_P03_2:
    temp_clk_hsiom = P3_2_SCB6_SPI_CLK;
    break;
  }

  return temp_clk_hsiom;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI获取MOSI引脚复用关系
// 参数说明       clk_pin     时钟引脚 参照 zf_driver_spi.h 内 spi_mosi_pin_enum
// 枚举体定义 返回参数       void 使用示例 spi_get_mosi_pin(SPI0_MOSI_P02_1);
// 备注信息       内部调用，用户无需关心
//-------------------------------------------------------------------------------------------------------------------
static en_hsiom_sel_t spi_get_mosi_hsiom(spi_mosi_pin_enum mosi_pin) {
  en_hsiom_sel_t temp_mosi_hsiom = HSIOM_SEL_GPIO;

  switch (mosi_pin) {
  case SPI0_MOSI_P02_1:
    temp_mosi_hsiom = P2_1_SCB7_SPI_MOSI;
    break;
  case SPI1_MOSI_P12_1:
    temp_mosi_hsiom = P12_1_SCB8_SPI_MOSI;
    break;
  case SPI2_MOSI_P15_1:
    temp_mosi_hsiom = P15_1_SCB9_SPI_MOSI;
    break;
  case SPI6_MOSI_P03_1:
    temp_mosi_hsiom = P3_1_SCB6_SPI_MOSI;
    break;
  }

  return temp_mosi_hsiom;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI获取MISO引脚复用关系
// 参数说明       miso_pin     时钟引脚 参照 zf_driver_spi.h 内
// spi_miso_pin_enum 枚举体定义 返回参数       void 使用示例
// spi_get_miso_pin(SPI0_MISO_P02_0); 备注信息       内部调用，用户无需关心
//-------------------------------------------------------------------------------------------------------------------
static en_hsiom_sel_t spi_get_miso_hsiom(spi_miso_pin_enum miso_pin) {
  en_hsiom_sel_t temp_miso_hsiom = HSIOM_SEL_GPIO;

  switch (miso_pin) {
  case SPI0_MISO_P02_0:
    temp_miso_hsiom = P2_0_SCB7_SPI_MISO;
    break;
  case SPI1_MISO_P12_0:
    temp_miso_hsiom = P12_0_SCB8_SPI_MISO;
    break;
  case SPI2_MISO_P15_0:
    temp_miso_hsiom = P15_0_SCB9_SPI_MISO;
    break;
  case SPI6_MISO_P3_0:
    temp_miso_hsiom = P3_0_SCB6_SPI_MISO;
    break;
  }

  return temp_miso_hsiom;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI获取CS引脚复用关系
// 参数说明       cs_pin     时钟引脚 参照 zf_driver_spi.h 内 spi_clk_pin_enum
// 枚举体定义 返回参数       void 使用示例 spi_get_clk_pin(SPI0_CS0_P02_3);
// 备注信息       内部调用，用户无需关心
//-------------------------------------------------------------------------------------------------------------------
static en_hsiom_sel_t spi_get_cs_hsiom(spi_cs_pin_enum cs_pin) {
  en_hsiom_sel_t temp_cs_hsiom = HSIOM_SEL_GPIO;
  switch (cs_pin) {
  case SPI0_CS0_P02_3:
    temp_cs_hsiom = P2_3_SCB7_SPI_SELECT0;
    break;
  case SPI0_CS1_P02_4:
    temp_cs_hsiom = P2_4_SCB7_SPI_SELECT1;
    break;
  case SPI1_CS0_P12_3:
    temp_cs_hsiom = P12_3_SCB8_SPI_SELECT0;
    break;
  case SPI1_CS1_P12_4:
    temp_cs_hsiom = P12_4_SCB8_SPI_SELECT1;
    break;
  case SPI2_CS0_P15_3:
    temp_cs_hsiom = P15_3_SCB9_SPI_SELECT0;
    break;
  case SPI2_CS3_P05_1:
    temp_cs_hsiom = P5_1_SCB9_SPI_SELECT3;
    break;
  case SPI6_CS_P03_3:
    temp_cs_hsiom = P3_3_GPIO;
    break;
  }
  return temp_cs_hsiom;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI配置发送长度
// 参数说明       spi_n     SPI模块号 参照 zf_driver_spi.h 内 spi_index_enum
// 枚举体定义 参数说明       length		需要配置的长度 仅支持 8 16
// 返回参数       void
// 使用示例       spi_get_clk_pin(SPI_0, 8);
// 备注信息       内部调用，用户无需关心
//-------------------------------------------------------------------------------------------------------------------
static void switch_transition_length(spi_index_enum spi_n, uint8 length) {
  switch (length) {
  case 8: {
    spi_module[spi_n]->unCTRL.u32Register &= 0xffff3fff;
    spi_module[spi_n]->unTX_CTRL.u32Register &= 0xffffffe0;
    spi_module[spi_n]->unTX_CTRL.u32Register |= 0x00000007;
    spi_module[spi_n]->unRX_CTRL.u32Register &= 0xffffffe0;
    spi_module[spi_n]->unRX_CTRL.u32Register |= 0x00000007;
  } break;
  case 16: {
    spi_module[spi_n]->unCTRL.u32Register &= 0xffff3fff;
    spi_module[spi_n]->unCTRL.u32Register |= 0x00004000;
    spi_module[spi_n]->unTX_CTRL.u32Register &= 0xffffffe0;
    spi_module[spi_n]->unTX_CTRL.u32Register |= 0x0000000F;
    spi_module[spi_n]->unRX_CTRL.u32Register &= 0xffffffe0;
    spi_module[spi_n]->unRX_CTRL.u32Register |= 0x0000000F;
  } break;
  }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口写 8bit 数据
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       data            数据 返回参数 void
// 使用示例       spi_write_8bit(SPI_0, 0x11);
// 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_write_8bit(spi_index_enum spi_n, const uint8 data) {
  switch_transition_length(spi_n, 8);    // 切换通信长度为8位
  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  Cy_SCB_WriteTxFifo(spi_module[spi_n], data); // 发送数据
  while (Cy_SCB_GetFifoSize(spi_module[spi_n]) ==
         Cy_SCB_GetNumInTxFifo(spi_module[spi_n]))
    ; // 缓冲区满则等待

  while (Cy_SCB_IsTxComplete(spi_module[spi_n]) == 0)
    ; // 等待数据发送完成

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口写 8bit 数组
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       *data           数据存放缓冲区
// 参数说明       len             缓冲区长度
// 返回参数       void
// 使用示例       spi_write_8bit_array(SPI_0, data, 64);
// 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_write_8bit_array(spi_index_enum spi_n, const uint8 *data, uint32 len) {
  switch_transition_length(spi_n, 8); // 切换通信长度为8位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  do {
    Cy_SCB_WriteTxFifo(spi_module[spi_n], *data++); // 发送数据
    while (Cy_SCB_GetFifoSize(spi_module[spi_n]) ==
           Cy_SCB_GetNumInTxFifo(spi_module[spi_n]))
      ;       // 缓冲区满则等待
    len -= 1; // 发送长度自减
  } while (len);

  while (Cy_SCB_IsTxComplete(spi_module[spi_n]) == 0)
    ; // 等待数据发送完成

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口写 16bit 数据
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       data            数据 返回参数 void
// 使用示例       spi_write_16bit(SPI_0, 0x1101);
// 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_write_16bit(spi_index_enum spi_n, const uint16 data) {
  switch_transition_length(spi_n, 16); // 切换通信长度为16位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  Cy_SCB_WriteTxFifo(spi_module[spi_n], data); // 发送数据
  while (Cy_SCB_GetFifoSize(spi_module[spi_n]) ==
         Cy_SCB_GetNumInTxFifo(spi_module[spi_n]))
    ; // 缓冲区满则等待

  while (Cy_SCB_IsTxComplete(spi_module[spi_n]) == 0)
    ; // 等待数据发送完成

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口写 16bit 数组
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       *data           数据存放缓冲区
// 参数说明       len             缓冲区长度
// 返回参数       void
// 使用示例       spi_write_16bit_array(SPI_0, data, 64);
// 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_write_16bit_array(spi_index_enum spi_n, const uint16 *data,
                           uint32 len) {
  switch_transition_length(spi_n, 16); // 切换通信长度为16位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  do {
    Cy_SCB_WriteTxFifo(spi_module[spi_n], *data++); // 发送数据
    while (Cy_SCB_GetFifoSize(spi_module[spi_n]) ==
           Cy_SCB_GetNumInTxFifo(spi_module[spi_n]))
      ;       // 缓冲区满则等待
    len -= 1; // 发送长度自减
  } while (len);

  while (Cy_SCB_IsTxComplete(spi_module[spi_n]) == 0)
    ; // 等待数据发送完成

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口向传感器的寄存器写 8bit 数据
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       register_name   寄存器地址 参数说明
// data            数据 返回参数       void 使用示例
// spi_write_8bit_register(SPI_0, 0x11, 0x01); 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_write_8bit_register(spi_index_enum spi_n, const uint8 register_name,
                             const uint8 data) {
  switch_transition_length(spi_n, 8); // 切换通信长度为8位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  Cy_SCB_WriteTxFifo(spi_module[spi_n], register_name); // 发送寄存器地址
  while (Cy_SCB_GetFifoSize(spi_module[spi_n]) ==
         Cy_SCB_GetNumInTxFifo(spi_module[spi_n]))
    ; // 缓冲区满则等待

  Cy_SCB_WriteTxFifo(spi_module[spi_n], data); // 发送数据
  while (Cy_SCB_GetFifoSize(spi_module[spi_n]) ==
         Cy_SCB_GetNumInTxFifo(spi_module[spi_n]))
    ; // 缓冲区满则等待

  while (Cy_SCB_IsTxComplete(spi_module[spi_n]) == 0)
    ; // 等待数据发送完成

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口向传感器的寄存器写 8bit 数组
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       register_name   寄存器地址 参数说明
// *data           数据存放缓冲区 参数说明       len             缓冲区长度
// 返回参数       void
// 使用示例       spi_write_8bit_registers(SPI_0, 0x11, data, 32);
// 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_write_8bit_registers(spi_index_enum spi_n, const uint8 register_name,
                              const uint8 *data, uint32 len) {
  switch_transition_length(spi_n, 8); // 切换通信长度为8位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  Cy_SCB_WriteTxFifo(spi_module[spi_n], register_name); // 发送寄存器地址
  while (Cy_SCB_GetFifoSize(spi_module[spi_n]) ==
         Cy_SCB_GetNumInTxFifo(spi_module[spi_n]))
    ; // 缓冲区满则等待

  do {
    Cy_SCB_WriteTxFifo(spi_module[spi_n], *data++); // 发送数据
    while (Cy_SCB_GetFifoSize(spi_module[spi_n]) ==
           Cy_SCB_GetNumInTxFifo(spi_module[spi_n]))
      ;       // 缓冲区满则等待
    len -= 1; // 发送长度自减
  } while (len);

  while (Cy_SCB_IsTxComplete(spi_module[spi_n]) == 0)
    ; // 等待数据发送完成

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口向传感器的寄存器写 16bit 数据
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       register_name   寄存器地址 参数说明
// data            数据 返回参数       void 使用示例
// spi_write_16bit_register(SPI_0, 0x1011, 0x0101); 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_write_16bit_register(spi_index_enum spi_n, const uint16 register_name,
                              const uint16 data) {
  switch_transition_length(spi_n, 16); // 切换通信长度为16位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  Cy_SCB_WriteTxFifo(spi_module[spi_n], register_name); // 发送寄存器地址
  while (Cy_SCB_GetFifoSize(spi_module[spi_n]) ==
         Cy_SCB_GetNumInTxFifo(spi_module[spi_n]))
    ; // 缓冲区满则等待

  Cy_SCB_WriteTxFifo(spi_module[spi_n], data); // 发送数据
  while (Cy_SCB_GetFifoSize(spi_module[spi_n]) ==
         Cy_SCB_GetNumInTxFifo(spi_module[spi_n]))
    ; // 缓冲区满则等待

  while (Cy_SCB_IsTxComplete(spi_module[spi_n]) == 0)
    ; // 等待数据发送完成

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口向传感器的寄存器写 16bit 数组
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       register_name   寄存器地址 参数说明
// *data           数据存放缓冲区 参数说明       len             缓冲区长度
// 返回参数       void
// 使用示例       spi_write_16bit_registers(SPI_0, 0x1011, data, 32);
// 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_write_16bit_registers(spi_index_enum spi_n, const uint16 register_name,
                               const uint16 *data, uint32 len) {
  switch_transition_length(spi_n, 16); // 切换通信长度为16位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  Cy_SCB_WriteTxFifo(spi_module[spi_n], register_name); // 发送寄存器地址
  while (Cy_SCB_GetFifoSize(spi_module[spi_n]) ==
         Cy_SCB_GetNumInTxFifo(spi_module[spi_n]))
    ; // 缓冲区满则等待

  do {
    Cy_SCB_WriteTxFifo(spi_module[spi_n], *data++); // 发送数据
    while (Cy_SCB_GetFifoSize(spi_module[spi_n]) ==
           Cy_SCB_GetNumInTxFifo(spi_module[spi_n]))
      ;       // 缓冲区满则等待
    len -= 1; // 发送长度自减
  } while (len);

  while (Cy_SCB_IsTxComplete(spi_module[spi_n]) == 0)
    ; // 等待数据发送完成

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口读 8bit 数据
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       register_name   寄存器地址 返回参数
// uint8           数据 使用示例       spi_read_8bit(SPI_0); 备注信息
//-------------------------------------------------------------------------------------------------------------------
uint8 spi_read_8bit(spi_index_enum spi_n) {
  uint8 read_data = 0;

  Cy_SCB_SPI_ClearRxFifo(spi_module[spi_n]); // 清除接收缓冲区

  switch_transition_length(spi_n, 8); // 切换单次通信长度为8位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  Cy_SCB_WriteTxFifo(spi_module[spi_n], 0); // 发送空数据
  while (Cy_SCB_SPI_GetNumInRxFifo(spi_module[spi_n]) == 0)
    ; // 等待接收到数据

  read_data = (uint8)(spi_module[spi_n]->unRX_FIFO_RD.u32Register); // 读取数据

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }

  return read_data;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口读 8bit 数组
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       *data           数据存放缓冲区
// 参数说明       len             发送缓冲区长度
// 返回参数       void
// 使用示例       spi_read_8bit_array(SPI_0, data, 64);
// 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_read_8bit_array(spi_index_enum spi_n, uint8 *data, uint32 len) {
  Cy_SCB_SPI_ClearRxFifo(spi_module[spi_n]); // 清除接收缓冲区

  switch_transition_length(spi_n, 8); // 切换单次通信长度为8位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  do {
    Cy_SCB_WriteTxFifo(spi_module[spi_n], 0); // 发送空数据
    while (Cy_SCB_SPI_GetNumInRxFifo(spi_module[spi_n]) == 0)
      ; // 等待接收到数据
    *data++ = (uint8)(spi_module[spi_n]->unRX_FIFO_RD.u32Register); // 读取数据
    len -= 1;
  } while (len);

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口读 16bit 数据
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       register_name   寄存器地址 返回参数
// uint16          数据 使用示例       spi_read_16bit(SPI_0); 备注信息
//-------------------------------------------------------------------------------------------------------------------
uint16 spi_read_16bit(spi_index_enum spi_n) {
  uint16 read_data = 0;

  Cy_SCB_SPI_ClearRxFifo(spi_module[spi_n]); // 清除接收缓冲区

  switch_transition_length(spi_n, 16); // 切换单次通信长度为16位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  Cy_SCB_WriteTxFifo(spi_module[spi_n], 0); // 发送空数据
  while (Cy_SCB_SPI_GetNumInRxFifo(spi_module[spi_n]) == 0)
    ; // 等待接收到数据
  read_data = (uint16)(spi_module[spi_n]->unRX_FIFO_RD.u32Register); // 读取数据

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }

  return read_data;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口读 16bit 数组
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       *data           数据存放缓冲区
// 参数说明       len             发送缓冲区长度
// 返回参数       void
// 使用示例       spi_read_16bit_array(SPI_0, data, 64);
// 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_read_16bit_array(spi_index_enum spi_n, uint16 *data, uint32 len) {
  Cy_SCB_SPI_ClearRxFifo(spi_module[spi_n]); // 清除接收缓冲区

  switch_transition_length(spi_n, 16); // 切换单次通信长度为16位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  do {
    Cy_SCB_WriteTxFifo(spi_module[spi_n], 0); // 发送空数据
    while (Cy_SCB_SPI_GetNumInRxFifo(spi_module[spi_n]) == 0)
      ; // 等待接收到数据
    *data++ = (uint16)(spi_module[spi_n]->unRX_FIFO_RD.u32Register); // 读取数据
    len -= 1;
  } while (len);

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口从传感器的寄存器读 8bit 数据
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       register_name   寄存器地址 返回参数
// uint8           数据 使用示例       spi_read_8bit_register(SPI_0, 0x11);
// 备注信息
//-------------------------------------------------------------------------------------------------------------------
uint8 spi_read_8bit_register(spi_index_enum spi_n, const uint8 register_name) {
  uint8 read_data = 0;

  switch_transition_length(spi_n, 8); // 切换单次通信长度为8位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  Cy_SCB_WriteTxFifo(spi_module[spi_n], register_name); // 发送寄存器地址
  while (Cy_SCB_SPI_GetNumInRxFifo(spi_module[spi_n]) == 0)
    ; // 等待接收到数据

  Cy_SCB_SPI_ClearRxFifo(spi_module[spi_n]); // 清除接收缓冲区

  Cy_SCB_WriteTxFifo(spi_module[spi_n], 0); // 发送空数据
  while (Cy_SCB_SPI_GetNumInRxFifo(spi_module[spi_n]) == 0)
    ; // 等待接收到数据
  read_data = (uint8)(spi_module[spi_n]->unRX_FIFO_RD.u32Register); // 读取数据

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }

  return read_data;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口从传感器的寄存器读 8bit 数组
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       register_name   寄存器地址 参数说明
// *data           数据存放缓冲区 参数说明       len             发送缓冲区长度
// 返回参数       void
// 使用示例       spi_read_8bit_registers(SPI_0, 0x11, data, 32);
// 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_read_8bit_registers(spi_index_enum spi_n, const uint8 register_name,
                             uint8 *data, uint32 len) {
  switch_transition_length(spi_n, 8); // 切换单次通信长度为8位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  Cy_SCB_WriteTxFifo(spi_module[spi_n], register_name); // 发送寄存器地址
  while (Cy_SCB_SPI_GetNumInRxFifo(spi_module[spi_n]) == 0)
    ; // 等待接收到数据

  Cy_SCB_SPI_ClearRxFifo(spi_module[spi_n]); // 清除接收缓冲区

  do {
    Cy_SCB_WriteTxFifo(spi_module[spi_n], 0); // 发送空数据
    while (Cy_SCB_SPI_GetNumInRxFifo(spi_module[spi_n]) == 0)
      ; // 等待接收到数据
    *data++ = (uint8)(spi_module[spi_n]->unRX_FIFO_RD.u32Register); // 读取数据
    len -= 1;
  } while (len);

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口从传感器的寄存器读 16bit 数据
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       register_name   寄存器地址 返回参数
// uint16          数据 使用示例       spi_read_16bit_register(SPI_0, 0x1011);
// 备注信息
//-------------------------------------------------------------------------------------------------------------------
uint16 spi_read_16bit_register(spi_index_enum spi_n,
                               const uint16 register_name) {
  uint8 read_data = 0;

  switch_transition_length(spi_n, 16); // 切换单次通信长度为16位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  Cy_SCB_WriteTxFifo(spi_module[spi_n], register_name); // 发送寄存器地址
  while (Cy_SCB_SPI_GetNumInRxFifo(spi_module[spi_n]) == 0)
    ; // 等待接收到数据

  Cy_SCB_SPI_ClearRxFifo(spi_module[spi_n]); // 清除接收缓冲区

  Cy_SCB_WriteTxFifo(spi_module[spi_n], 0); // 发送空数据
  while (Cy_SCB_SPI_GetNumInRxFifo(spi_module[spi_n]) == 0)
    ; // 等待接收到数据
  read_data = (uint16)(spi_module[spi_n]->unRX_FIFO_RD.u32Register); // 读取数据

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }

  return read_data;
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 接口从传感器的寄存器读 16bit 数组
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       register_name   寄存器地址 参数说明
// *data           数据存放缓冲区 参数说明       len             发送缓冲区长度
// 返回参数       void
// 使用示例       spi_read_16bit_registers(SPI_0, 0x1101, data, 32);
// 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_read_16bit_registers(spi_index_enum spi_n, const uint16 register_name,
                              uint16 *data, uint32 len) {
  switch_transition_length(spi_n, 16); // 切换单次通信长度为16位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  Cy_SCB_WriteTxFifo(spi_module[spi_n], register_name); // 发送寄存器地址
  while (Cy_SCB_SPI_GetNumInRxFifo(spi_module[spi_n]) == 0)
    ; // 等待接收到数据

  Cy_SCB_SPI_ClearRxFifo(spi_module[spi_n]); // 清除接收缓冲区

  do {
    Cy_SCB_WriteTxFifo(spi_module[spi_n], 0); // 发送空数据
    while (Cy_SCB_SPI_GetNumInRxFifo(spi_module[spi_n]) == 0)
      ; // 等待接收到数据
    *data++ = (uint16)(spi_module[spi_n]->unRX_FIFO_RD.u32Register); // 读取数据
    len -= 1;
  } while (len);

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 8bit 数据传输 发送与接收数据是同时进行的
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       write_buffer    发送的数据缓冲区地址
// 参数说明       read_buffer 发送数据时接收到的数据的存储地址(不需要接收则传
// NULL) 参数说明       len             缓冲区长度 返回参数       void 使用示例
// spi_transfer_8bit(SPI_0, buf, buf, 1); 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_transfer_8bit(spi_index_enum spi_n, const uint8 *write_buffer,
                       uint8 *read_buffer, uint32 len) {
  switch_transition_length(spi_n, 8); // 切换单次通信长度为8位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  Cy_SCB_SPI_ClearRxFifo(spi_module[spi_n]); // 清除接收缓冲区

  do {
    Cy_SCB_WriteTxFifo(spi_module[spi_n], *write_buffer++); // 发送数据
    while (Cy_SCB_SPI_GetNumInRxFifo(spi_module[spi_n]) == 0)
      ; // 等待接收到数据
    *read_buffer++ =
        (uint8)(spi_module[spi_n]->unRX_FIFO_RD.u32Register); // 读取数据
    len -= 1;
  } while (len);

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }
}

//-------------------------------------------------------------------------------------------------------------------
// 函数简介       SPI 16bit 数据传输 发送与接收数据是同时进行的
// 参数说明       spi_n           SPI 模块号 参照 zf_driver_spi.h 内
// spi_index_enum 枚举体定义 参数说明       write_buffer    发送的数据缓冲区地址
// 参数说明       read_buffer 发送数据时接收到的数据的存储地址(不需要接收则传
// NULL) 参数说明       len             缓冲区长度 返回参数       void 使用示例
// spi_transfer_16bit(SPI_0, buf, buf, 1); 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_transfer_16bit(spi_index_enum spi_n, const uint16 *write_buffer,
                        uint16 *read_buffer, uint32 len) {
  switch_transition_length(spi_n, 16); // 切换单次通信长度为16位

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉低CS
  {
    gpio_low(cs_pin_save[spi_n]);
  }

  Cy_SCB_SPI_ClearRxFifo(spi_module[spi_n]); // 清除接收缓冲区

  do {
    Cy_SCB_WriteTxFifo(spi_module[spi_n], *write_buffer++); // 发送数据
    while (Cy_SCB_SPI_GetNumInRxFifo(spi_module[spi_n]) == 0)
      ; // 等待接收到数据
    *read_buffer++ =
        (uint16)(spi_module[spi_n]->unRX_FIFO_RD.u32Register); // 读取数据
    len -= 1;
  } while (len);

  if (cs_pin_save[spi_n] != SPI_CS_NULL) // 若CS不为空 则拉高CS
  {
    gpio_high(cs_pin_save[spi_n]);
  }
}

//-------------------------------------------------------------------------------------------------------------------
//  函数简介      SPI初始化
//  参数说明      spi_n           选择SPI模块(SPI_0-SPI_2)
//  参数说明      mode            SPI模式 0：CPOL=0 CPHA=0    1：CPOL=0 CPHA=1
//  2：CPOL=1 CPHA=0   3：CPOL=1 CPHA=1  // 具体细节可自行查阅资料 参数说明 baud
//  设置SPI的波特率 参数说明      cs_pin          选择SPI片选引脚 参数说明
//  sck_pin         选择SPI时钟引脚 参数说明      mosi_pin        选择SPI
//  MOSI引脚 参数说明      miso_pin        选择SPI MISO引脚 返回参数      void
//  使用示例      spi_init(SPI_0, SPI_MODE0, 1*1000*1000, SPI0_CLK_P2_2,
//  SPI0_MOSI_P2_1, SPI0_MISO_P2_0, SPI0_CS0_P2_3); // 硬件SPI初始化  模式0
//  波特率为1Mhz 备注信息
//-------------------------------------------------------------------------------------------------------------------
void spi_init(spi_index_enum spi_n, spi_mode_enum mode, uint32 baud,
              spi_clk_pin_enum clk_pin, spi_mosi_pin_enum mosi_pin,
              spi_miso_pin_enum miso_pin, spi_cs_pin_enum cs_pin) {
  uint64_t targetFreq = 4 * baud;
  uint64_t sourceFreq_fp5 = ((uint64_t)SPI_FREQ << 5ull);
  uint32_t divSetting_fp5 = (uint32_t)(sourceFreq_fp5 / targetFreq);
  cy_stc_gpio_pin_config_t spi_pin_cfg = {0};
  cy_stc_scb_spi_config_t spi_config = {0};

  // 醒醒，模块号和端口都不对应怎么能初始化呢？
  zf_assert((uint8)spi_n == (uint8)clk_pin ? 1 : 0);
  zf_assert((uint8)clk_pin == (uint8)mosi_pin ? 1 : 0);

  spi_pin_cfg.driveMode = CY_GPIO_DM_STRONG_IN_OFF;
  spi_pin_cfg.hsiom = spi_get_mosi_hsiom(mosi_pin);
  Cy_GPIO_Pin_Init(get_port(spi_get_mosi_pin(mosi_pin)),
                   (spi_get_mosi_pin(mosi_pin) % 8), &spi_pin_cfg);

  spi_pin_cfg.driveMode = CY_GPIO_DM_STRONG_IN_OFF;
  spi_pin_cfg.hsiom = spi_get_clk_hsiom(clk_pin);
  Cy_GPIO_Pin_Init(get_port(spi_get_clk_pin(clk_pin)),
                   (spi_get_clk_pin(clk_pin) % 8), &spi_pin_cfg);

  if (SPI_MISO_NULL != miso_pin) {
    spi_pin_cfg.driveMode = CY_GPIO_DM_HIGHZ;
    spi_pin_cfg.hsiom = spi_get_miso_hsiom(miso_pin);
    Cy_GPIO_Pin_Init(get_port(spi_get_miso_pin(miso_pin)),
                     (spi_get_miso_pin(miso_pin) % 8), &spi_pin_cfg);
  }
  if (SPI_CS_NULL != cs_pin) {
    spi_pin_cfg.driveMode = CY_GPIO_DM_STRONG_IN_OFF;
    spi_pin_cfg.hsiom = spi_get_cs_hsiom(cs_pin);
    Cy_GPIO_Pin_Init(get_port(spi_get_cs_pin(cs_pin)),
                     (spi_get_cs_pin(cs_pin) % 8), &spi_pin_cfg);
  }
  // IMU所在SPI不在逐飞库的使用方法范围内，因此进行特判
  if (spi_n != SPI_6) {
    Cy_SysClk_PeriphAssignDivider(
        (en_clk_dst_t)((uint32)PCLK_SCB7_CLOCK + (uint32)spi_n),
        CY_SYSCLK_DIV_24_5_BIT, 1ul);
    Cy_SysClk_PeriphSetFracDivider(
        Cy_SysClk_GetClockGroup(
            (en_clk_dst_t)((uint32)PCLK_SCB7_CLOCK + (uint32)spi_n)),
        CY_SYSCLK_DIV_24_5_BIT, 1ul, ((divSetting_fp5 & 0x1FFFFFE0ul) >> 5ul),
        (divSetting_fp5 & 0x0000001Ful));
    Cy_SysClk_PeriphEnableDivider(
        Cy_SysClk_GetClockGroup(
            (en_clk_dst_t)((uint32)PCLK_SCB7_CLOCK + (uint32)spi_n)),
        CY_SYSCLK_DIV_24_5_BIT, 1ul);
  } else if (spi_n == SPI_6) {
    Cy_SysClk_PeriphAssignDivider((en_clk_dst_t)((uint32)PCLK_SCB6_CLOCK),
                                  CY_SYSCLK_DIV_24_5_BIT, 1ul);
    Cy_SysClk_PeriphSetFracDivider(
        Cy_SysClk_GetClockGroup((en_clk_dst_t)((uint32)PCLK_SCB6_CLOCK)),
        CY_SYSCLK_DIV_24_5_BIT, 1ul, ((divSetting_fp5 & 0x1FFFFFE0ul) >> 5ul),
        (divSetting_fp5 & 0x0000001Ful));
    Cy_SysClk_PeriphEnableDivider(
        Cy_SysClk_GetClockGroup((en_clk_dst_t)((uint32)PCLK_SCB6_CLOCK)),
        CY_SYSCLK_DIV_24_5_BIT, 1ul);
  }
  spi_config.spiMode = CY_SCB_SPI_MASTER;
  spi_config.subMode = CY_SCB_SPI_MOTOROLA;
  switch (mode) {
  case SPI_MODE0:
    /* code */
    spi_config.sclkMode = CY_SCB_SPI_CPHA0_CPOL0;
    break;
  case SPI_MODE3:
    spi_config.sclkMode = CY_SCB_SPI_CPHA1_CPOL1;
    break;
  default:
    break;
  }
  spi_config.oversample = 4;
  spi_config.rxDataWidth = 8;
  spi_config.txDataWidth = 8;
  spi_config.enableMsbFirst = true;
  spi_config.enableMisoLateSample = true;

  Cy_SCB_SPI_Init(spi_module[spi_n], &spi_config, NULL);
  Cy_SCB_SPI_SetActiveSlaveSelect(spi_module[spi_n], 0ul);
  Cy_SCB_SPI_Enable(spi_module[spi_n]);

  cs_pin_save[spi_n] = cs_pin;
}
